﻿using System;
using System.IO;
using System.Collections.Generic;
using System.Reflection;
using System.Linq;
using System.Text;

namespace PickGold
{
	/// <summary>
	/// 限制长度流
	/// </summary>
	public class LimitStream : Stream
	{
		private Stream Data;
		private long Index;
		private long Limit;
		private bool Open;

		/// <summary>
		/// 
		/// </summary>
		/// <param name="stream"></param>
		/// <param name="limit"></param>
		/// <param name="liveOpen"></param>
		public LimitStream(Stream stream, long limit, bool liveOpen)
		{
			this.Data = stream;
			this.Index = 0;
			this.Limit = limit;
			this.Open = liveOpen;
		}

		/// <summary>
		/// 
		/// </summary>
		public override bool CanRead
		{
			get
			{
				if (this.Data == null)
					throw new ObjectDisposedException(typeof(LimitStream).Name);

				return this.Data.CanRead;
			}
		}

		/// <summary>
		/// 
		/// </summary>
		public override bool CanSeek
		{
			get
			{
				if (this.Data == null)
					throw new ObjectDisposedException(typeof(LimitStream).Name);

				return this.Data.CanSeek;
			}
		}

		/// <summary>
		/// 
		/// </summary>
		public override bool CanWrite
		{
			get
			{
				if (this.Data == null)
					throw new ObjectDisposedException(typeof(LimitStream).Name);

				return this.Data.CanWrite;
			}
		}

		/// <summary>
		/// 
		/// </summary>
		public override long Length
		{
			get
			{
				if (this.Data == null)
					throw new ObjectDisposedException(typeof(LimitStream).Name);

				return this.Limit;//return this.Data.Length;
			}
		}

		/// <summary>
		/// 
		/// </summary>
		public override long Position
		{
			get
			{
				if (this.Data == null)
					throw new ObjectDisposedException(typeof(LimitStream).Name);

				return this.Index;
			}
			set
			{
				if (this.Data == null)
					throw new ObjectDisposedException(typeof(LimitStream).Name);

				if (value < 0 || value > this.Limit)
					throw new ArgumentOutOfRangeException("value");

				this.Data.Position = this.Data.Position - this.Index + value;//this.Data.Position = value;
				this.Index = value;
			}
		}

		/// <summary>
		/// 
		/// </summary>
		public override void Flush()
		{
			if (this.Data == null)
				throw new ObjectDisposedException(typeof(LimitStream).Name);

			this.Data.Flush();
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="buffer"></param>
		/// <param name="offset"></param>
		/// <param name="count"></param>
		/// <returns></returns>
		public override int Read(byte[] buffer, int offset, int count)
		{
			if (this.Data == null)
				throw new ObjectDisposedException(typeof(LimitStream).Name);

			if (count < 0)
				count = buffer.Length;
			if (count > this.Limit - this.Index)
				count = (int)(this.Limit - this.Index);
			count = this.Data.Read(buffer, offset, count);
			this.Index += count;
			return count;
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="offset"></param>
		/// <param name="origin"></param>
		/// <returns></returns>
		public override long Seek(long offset, SeekOrigin origin)
		{
			if (this.Data == null)
				throw new ObjectDisposedException(typeof(LimitStream).Name);

			switch (origin)
			{
				case SeekOrigin.Begin:
					if (offset < 0 || offset > this.Limit)
						throw new ArgumentOutOfRangeException(MethodBase.GetCurrentMethod().GetParameters()[0].Name);

					this.Data.Position = this.Data.Position - this.Index + offset;
					this.Index = offset;
					break;
				case SeekOrigin.Current:
					offset = offset + this.Index;
					if (offset < 0 || offset > this.Limit)
						throw new ArgumentOutOfRangeException(MethodBase.GetCurrentMethod().GetParameters()[0].Name);

					this.Data.Position = this.Data.Position - this.Index + offset;
					this.Index = offset;
					break;
				case SeekOrigin.End:
					offset = offset + this.Limit;
					if (offset < 0 || offset > this.Limit)
						throw new ArgumentOutOfRangeException(MethodBase.GetCurrentMethod().GetParameters()[0].Name);

					this.Data.Position = this.Data.Position - this.Index + offset;
					this.Index = offset;
					break;
				default:
					throw new ArgumentOutOfRangeException(MethodBase.GetCurrentMethod().GetParameters()[1].Name);
			}
			return this.Index;
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="value"></param>
		public override void SetLength(long value)
		{
			if (this.Data == null)
				throw new ObjectDisposedException(typeof(LimitStream).Name);

			this.Limit = value;
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="buffer"></param>
		/// <param name="offset"></param>
		/// <param name="count"></param>
		public override void Write(byte[] buffer, int offset, int count)
		{
			if (this.Data == null)
				throw new ObjectDisposedException(typeof(LimitStream).Name);

			if (count < 0)
				count = buffer.Length;
			if (count > this.Limit - this.Index)
				count = (int)(this.Limit - this.Index);
			this.Data.Write(buffer, offset, count);
			this.Index += count;
		}

		/// <summary>
		/// 
		/// </summary>
		public override void Close()
		{
			if (this.Data == null)
				return;

			if (this.Open)
				return;

			this.Data.Close();
			this.Data = null;
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="disposing"></param>
		protected override void Dispose(bool disposing)
		{
			this.Close();
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="stream"></param>
		/// <param name="limit"></param>
		/// <param name="liveOpen"></param>
		public void Reset(Stream stream, long limit, bool liveOpen)
		{
			this.Data = stream;
			this.Index = 0;
			this.Limit = limit;
			this.Open = liveOpen;
		}
	}
}
